package jfxtras.scene.control;


import java.util.Calendar;
import java.util.Date;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Popup;
import jfxtras.scene.NodeUtil;

import com.sun.javafx.scene.control.skin.SkinBase;

/**
 * 
 * @author Tom Eugelink
 * 
 * Possible extension: drop down list or grid for quick selection
 */
public class CalendarTextFieldXCaspianSkin extends SkinBase<CalendarTextFieldX, CalendarTextFieldXBehavior>
{
	// ==================================================================================================================
	// CONSTRUCTOR
	
	/**
	 * 
	 */
	public CalendarTextFieldXCaspianSkin(CalendarTextFieldX control)
	{
		super(control, new CalendarTextFieldXBehavior(control));
		construct();
	}

	/*
	 * 
	 */
	private void construct()
	{
		// setup component
		createNodes();
		
		// react to value changes in the model
		getSkinnable().valueProperty().addListener(new ChangeListener<Calendar>()
		{
			@Override
			public void changed(ObservableValue<? extends Calendar> observableValue, Calendar oldValue, Calendar newValue)
			{
				refreshValue();
			}
		});
		refreshValue();
	}
	
	/*
	 * 
	 */
	private void refreshValue()
	{
		// write out to textfield
		Calendar c = getSkinnable().getValue();
		String s = c == null ? "" : getSkinnable().getDateFormat().format( c.getTime() );
System.out.println("refreshValue setText " + s);		
		textField.setText( s );
	}
	
	// ==================================================================================================================
	// DRAW
	
	/**
	 * construct the nodes
	 */
	private void createNodes()
	{
		// the main textField
		textField = new TextField();
		textField.focusedProperty().addListener(new InvalidationListener()
		{			
			@Override
			public void invalidated(Observable arg0)
			{
				if (textField.isFocused() == false) {
					parse();
				}
			}
		});
		textField.setOnAction(new EventHandler<ActionEvent>()
		{
			@Override
			public void handle(ActionEvent evt)
			{
				parse();
			}
		});


		// the icon
		Image lImage = new Image(this.getClass().getResourceAsStream(this.getClass().getSimpleName() + "Icon.png"));
		imageView = new ImageView(lImage);
		imageView.setOnMouseClicked(new EventHandler<MouseEvent>()
		{
			@Override public void handle(MouseEvent evt)
			{
				showPopup(evt);
			}
		});
		
		// construct a gridpane: one row, two columns
		gridPane = new GridPane();
		gridPane.setHgap(3);
		gridPane.add(textField, 0, 0);
		gridPane.add(imageView, 1, 0);
		ColumnConstraints column0 = new ColumnConstraints(10, 10, Double.MAX_VALUE);
		column0.setHgrow(Priority.ALWAYS);
		gridPane.getColumnConstraints().addAll(column0); // first column gets any extra width
		
		// add to self
		this.getStyleClass().add(this.getClass().getSimpleName()); // always add self as style class, because CSS should relate to the skin not the control
		getChildren().add(gridPane);
		
		// focus
		this.focusedProperty().addListener(new ChangeListener<Boolean>()
		{
			@Override
			public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldValue, Boolean newValue)
			{
				System.out.println("focus " + oldValue + " -> " + newValue);
				if (newValue == true)
				{
					textField.requestFocus();
				}
			}
		});
		
		// prep the picker
		calendarPickerX = new CalendarPickerX();
		calendarPickerX.setMode(CalendarPickerX.Mode.SINGLE);		
		// bind our properties to the picker's 
		Bindings.bindBidirectional(calendarPickerX.locale(), getSkinnable().localeProperty()); // order is important, because the value of the first field is overwritten initially with the value of the last field
		Bindings.bindBidirectional(calendarPickerX.calendar(), getSkinnable().valueProperty()); // order is important, because the value of the first field is overwritten initially with the value of the last field 
	}
	private TextField textField = null;
	private ImageView imageView = null;
	private GridPane gridPane = null;
	private CalendarPickerX calendarPickerX = null;
	
	private void parse()
	{
		try
		{
			// get the text to parse
			String lText = textField.getText();
			
			// starts with - means substract days
			if (lText.startsWith("-") || lText.startsWith("+"))
			{
				// + has problems 
				if (lText.startsWith("+")) lText = lText.substring(1);
				
				// special units Day, Week, Month, Year
				// TODO: internationalize?
				int lUnit = Calendar.DATE;
				if (lText.toLowerCase().endsWith("d")) { lText = lText.substring(0, lText.length() - 1); lUnit = Calendar.DATE; }
				if (lText.toLowerCase().endsWith("w")) { lText = lText.substring(0, lText.length() - 1); lUnit = Calendar.WEEK_OF_YEAR; }
				if (lText.toLowerCase().endsWith("m")) { lText = lText.substring(0, lText.length() - 1); lUnit = Calendar.MONTH; }
				if (lText.toLowerCase().endsWith("y")) { lText = lText.substring(0, lText.length() - 1); lUnit = Calendar.YEAR; }
				
				// parse the delta
				int lDelta = Integer.parseInt(lText);
				Calendar lCalendar = (Calendar)getSkinnable().getValue().clone(); // TODO locale
				lCalendar.add(lUnit, lDelta);
				
				// set the value
				getSkinnable().setValue(lCalendar);
			}
			if (lText.equals("#"))
			{
				// set the value
				getSkinnable().setValue(Calendar.getInstance()); // TODO locale
			}
			else
			{
				// parse using the formatter
				Date lDate = getSkinnable().getDateFormat().parse( lText );
				Calendar lCalendar = Calendar.getInstance(); // TODO: how to get the correct locale
				lCalendar.setTime(lDate);
				
				// set the value
				getSkinnable().setValue(lCalendar);
			}
		}
		catch (Throwable t) 
		{ 
			// TODO: show error in popup
			t.printStackTrace();
		} 
	}
	
	/*
	 * 
	 */
	private void showPopup(MouseEvent evt)
	{
		System.out.println("showPopup");
		// create popup
		Popup lPopup = new Popup();
		lPopup.setAutoFix(true);
		lPopup.setAutoHide(true);
		lPopup.setHideOnEscape(true);
		lPopup.getContent().add(calendarPickerX);

		// semi transparent background
		calendarPickerX.setStyle("-fx-background-color: #A0A0A0A0"); // TODO: move this out to a CSS file
		
		// same width as the textfield (if possible)
		calendarPickerX.setPrefWidth(textField.getWidth());
		
		// show it just below the textfield
		lPopup.show(textField, NodeUtil.screenX(getSkinnable()), NodeUtil.screenY(getSkinnable()) + textField.getHeight());
		
		// get the focus over
		calendarPickerX.requestFocus();
	}
	
}
